/*-------------------------------------------------------------------------
 *
 * cryptohashfuncs.c
 *	  密码散列函数
 *
 * Portions Copyright (c) 2018-2022, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/cryptohashfuncs.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "common/cryptohash.h"
#include "common/md5.h"
#include "common/sha2.h"
#include "utils/builtins.h"


/*
 * MD5
 */

/* MD5 产生一个 16 字节（128 位）的哈希值；双倍用于十六进制 */
#define MD5_HASH_LEN  32

/*
 * 创建文本值的 MD5 哈希并将其作为十六进制字符串返回。
 */
Datum md5_text(PG_FUNCTION_ARGS)
{
	text	   *fc_in_text = PG_GETARG_TEXT_PP(0);
	size_t		fc_len;
	char		fc_hexsum[MD5_HASH_LEN + 1];
	const char *fc_errstr = NULL;

	/* 使用 varlena 元数据计算缓冲区的长度 */
	fc_len = VARSIZE_ANY_EXHDR(fc_in_text);

	/* 获取哈希结果 */
	if (pg_md5_hash(VARDATA_ANY(fc_in_text), fc_len, fc_hexsum, &fc_errstr) == false)
		ereport(ERROR,
				(errcode(ERRCODE_INTERNAL_ERROR),
				 errmsg("could not compute %s hash: %s", "MD5",
						fc_errstr)));

	/* 转换为文本并返回 */
	PG_RETURN_TEXT_P(cstring_to_text(fc_hexsum));
}

/*
 * 创建一个 bytea 值的 MD5 哈希并将其返回为十六进制字符串。
 */
Datum md5_bytea(PG_FUNCTION_ARGS)
{
	bytea	   *fc_in = PG_GETARG_BYTEA_PP(0);
	size_t		fc_len;
	char		fc_hexsum[MD5_HASH_LEN + 1];
	const char *fc_errstr = NULL;

	fc_len = VARSIZE_ANY_EXHDR(fc_in);
	if (pg_md5_hash(VARDATA_ANY(fc_in), fc_len, fc_hexsum, &fc_errstr) == false)
		ereport(ERROR,
				(errcode(ERRCODE_INTERNAL_ERROR),
				 errmsg("could not compute %s hash: %s", "MD5",
						fc_errstr)));

	PG_RETURN_TEXT_P(cstring_to_text(fc_hexsum));
}

/*
 * 内部例程，用于计算给定 bytea 输入的 cryptohash。
 */
static inline bytea *
fc_cryptohash_internal(pg_cryptohash_type fc_type, bytea *fc_input)
{
	const uint8 *fc_data;
	const char *fc_typestr = NULL;
	int			fc_digest_len = 0;
	size_t		fc_len;
	pg_cryptohash_ctx *fc_ctx;
	bytea	   *fc_result;

	switch (fc_type)
	{
		case PG_SHA224:
			fc_typestr = "SHA224";
			fc_digest_len = PG_SHA224_DIGEST_LENGTH;
			break;
		case PG_SHA256:
			fc_typestr = "SHA256";
			fc_digest_len = PG_SHA256_DIGEST_LENGTH;
			break;
		case PG_SHA384:
			fc_typestr = "SHA384";
			fc_digest_len = PG_SHA384_DIGEST_LENGTH;
			break;
		case PG_SHA512:
			fc_typestr = "SHA512";
			fc_digest_len = PG_SHA512_DIGEST_LENGTH;
			break;
		case PG_MD5:
#ifdef FDDGM
		case PG_SM3:
#endif
		case PG_SHA1:
			elog(ERROR, "unsupported cryptohash type %d", fc_type);
			break;
	}

	fc_result = palloc0(fc_digest_len + VARHDRSZ);
	fc_len = VARSIZE_ANY_EXHDR(fc_input);
	fc_data = (unsigned char *) VARDATA_ANY(fc_input);

	fc_ctx = pg_cryptohash_create(fc_type);
	if (pg_cryptohash_init(fc_ctx) < 0)
		elog(ERROR, "could not initialize %s context: %s", fc_typestr,
			 pg_cryptohash_error(fc_ctx));
	if (pg_cryptohash_update(fc_ctx, fc_data, fc_len) < 0)
		elog(ERROR, "could not update %s context: %s", fc_typestr,
			 pg_cryptohash_error(fc_ctx));
	if (pg_cryptohash_final(fc_ctx, (unsigned char *) VARDATA(fc_result),
							fc_digest_len) < 0)
		elog(ERROR, "could not finalize %s context: %s", fc_typestr,
			 pg_cryptohash_error(fc_ctx));
	pg_cryptohash_free(fc_ctx);

	SET_VARSIZE(fc_result, fc_digest_len + VARHDRSZ);

	return fc_result;
}

/*
 * SHA-2 变种
 */

Datum sha224_bytea(PG_FUNCTION_ARGS)
{
	bytea	   *fc_result = fc_cryptohash_internal(PG_SHA224, PG_GETARG_BYTEA_PP(0));

	PG_RETURN_BYTEA_P(fc_result);
}

Datum sha256_bytea(PG_FUNCTION_ARGS)
{
	bytea	   *fc_result = fc_cryptohash_internal(PG_SHA256, PG_GETARG_BYTEA_PP(0));

	PG_RETURN_BYTEA_P(fc_result);
}

Datum sha384_bytea(PG_FUNCTION_ARGS)
{
	bytea	   *fc_result = fc_cryptohash_internal(PG_SHA384, PG_GETARG_BYTEA_PP(0));

	PG_RETURN_BYTEA_P(fc_result);
}

Datum sha512_bytea(PG_FUNCTION_ARGS)
{
	bytea	   *fc_result = fc_cryptohash_internal(PG_SHA512, PG_GETARG_BYTEA_PP(0));

	PG_RETURN_BYTEA_P(fc_result);
}
